/**
 * (created at 2011-2-18)
 */
package qiushuo.mysqlparser.recognizer.parser;

import static qiushuo.mysqlparser.recognizer.SQLTokenType.IDENTIFIER;
import static qiushuo.mysqlparser.recognizer.SQLTokenType.KW_AND;
import static qiushuo.mysqlparser.recognizer.SQLTokenType.KW_READ;
import static qiushuo.mysqlparser.recognizer.SQLTokenType.KW_RELEASE;
import static qiushuo.mysqlparser.recognizer.SQLTokenType.KW_SET;
import static qiushuo.mysqlparser.recognizer.SQLTokenType.KW_WITH;

import java.sql.SQLSyntaxErrorException;

import qiushuo.mysqlparser.ast.statement.MTLCommitStatement;
import qiushuo.mysqlparser.ast.statement.MTLStartTransactionStatement;
import qiushuo.mysqlparser.ast.statement.MTLTansactionLevelSetStatement;
import qiushuo.mysqlparser.recognizer.SQLToken;
import qiushuo.mysqlparser.recognizer.TokenReader;

/**
 * @author <a href="mailto:QiuShuo1985@gmail.com">QIU Shuo</a>
 */
public class MTLParser extends StatementParser {
    public MTLParser(TokenReader input) {
        super(input);
    }

    //BEGIN is not supported for transaction
    /** nothing has been pre-consumed */
    public MTLStartTransactionStatement startTransaction() throws SQLSyntaxErrorException {
        match(IDENTIFIER, true, "START");
        match(IDENTIFIER, true, "TRANSACTION");
        if (input.isNext(KW_WITH, 0)) {
            input.skipForce(3);
            return new MTLStartTransactionStatement(true);
        }
        return new MTLStartTransactionStatement(false);
    }

    /** nothing has been pre-consumed */
    public MTLCommitStatement commitOrRollback() throws SQLSyntaxErrorException {
        boolean no = false;
        boolean rollback = false;
        if (input.isNext(IDENTIFIER, 0, true, "ROLLBACK")) {
            rollback = true;
        } else {
            match(IDENTIFIER, true, "COMMIT");
        }
        if (input.isNext(IDENTIFIER, 0, true, "WORK")) {
            input.skipForce(1);
        }
        if (input.isNext(KW_AND, 0)) {
            input.skipForce(1);
            if (input.isNext(IDENTIFIER, 0, true, "NO")) {
                no = false;
                input.skipForce(1);
            }
            match(IDENTIFIER, true, "CHAIN");
            return new MTLCommitStatement(rollback, true, false, no);
        }
        if (input.isNext(IDENTIFIER, 0, true, "NO")) {
            no = false;
            input.skipForce(1);
        }
        if (input.isNext(KW_RELEASE, 0)) {
            input.skipForce(1);
            return new MTLCommitStatement(rollback, false, true, no);
        }
        return new MTLCommitStatement(rollback, false, false, false);
    }

    /** nothing has been pre-consumed */
    public MTLTansactionLevelSetStatement transactionIsolationSet() throws SQLSyntaxErrorException {
        match(KW_SET);
        int scope = MTLTansactionLevelSetStatement.SCOPE_NEXT;
        if (input.isNext(IDENTIFIER, 0, true, "GLOBAL")) {
            input.skipForce(1);
            scope = MTLTansactionLevelSetStatement.SCOPE_GLOBAL;
        } else if (input.isNext(IDENTIFIER, 0, true, "SESSION")) {
            input.skipForce(1);
            scope = MTLTansactionLevelSetStatement.SCOPE_SESSION;
        }
        match(IDENTIFIER, true, "TRANSACTION");
        match(IDENTIFIER, true, "ISOLATION");
        match(IDENTIFIER, true, "LEVEL");
        SQLToken t = input.next();
        String text;
        switch (t.getType()) {
        case IDENTIFIER:
            text = t.getText().toUpperCase();
            if ("REPEATABLE".equals(text)) {
                match(KW_READ);
                return new MTLTansactionLevelSetStatement(
                        scope,
                        MTLTansactionLevelSetStatement.ISOLATION_LEVEL_REPEATABLE_READ);
            } else if ("SERIALIZABLE".equals(text)) {
                return new MTLTansactionLevelSetStatement(
                        scope,
                        MTLTansactionLevelSetStatement.ISOLATION_LEVEL_SERIALIZABLE);
            }
            throw err("unexpected token: " + t);
        case KW_READ:
            text = match(IDENTIFIER).getText();
            if ("UNCOMMITTED".equals(text)) {
                return new MTLTansactionLevelSetStatement(
                        scope,
                        MTLTansactionLevelSetStatement.ISOLATION_LEVEL_READ_UNCOMMIT);
            } else if ("COMMITTED".equals(text)) {
                return new MTLTansactionLevelSetStatement(
                        scope,
                        MTLTansactionLevelSetStatement.ISOLATION_LEVEL_READ_COMMITED);
            }
        default:
            throw err("unexpected token: " + t);
        }
    }
}
